/**
 * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights
 * reserved. <a>http://www.cqcis.com</a>
 */
package com.cqcis.uip.base.logadaptor.impl;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.log4j.Appender;
import org.apache.log4j.DailyRollingFileAppender;
import org.apache.log4j.FileAppender;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.RollingFileAppender;

import com.cqcis.uip.base.logadaptor.LogConfiguration;
import com.cqcis.uip.base.logadaptor.LoggerAppenderRepository;

/**
 * @author huangmy
 * @date   2008-2-1
 */
public class LoggerAppenderRepositoryImpl implements LoggerAppenderRepository {
	private static LoggerAppenderRepository singleton = null;	
	private Map<Thread, List<LogConfiguration>> logConfigurations = new ConcurrentHashMap<Thread, List<LogConfiguration>>();
		
	private LoggerAppenderRepositoryImpl() {
		//default
	}
	
	public static synchronized LoggerAppenderRepository getLoggerAppenderRepository() {
		if (singleton == null) {
			singleton = new LoggerAppenderRepositoryImpl();
		}
		
		return singleton;
	}
	
	public Appender createOrGetAppenderFor(LogConfiguration logConfig, FileAppender tmplAppender) {
		Appender appender = logConfig.getLogObjectAppender();
		if (appender != null) {
			return appender;
		}
		
		try {
			appender = newFileAppender(logConfig, tmplAppender);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		
		if (appender == null) {
			throw new RuntimeException("Can't create new FileAppender.");
		}
		
		logConfig.setLogObjectAppender(appender);
		return appender;
	}
    
	/**
	 * 新建一个文件appender
	 * 
	 * @param thread
	 * @param templateAppender
	 * @param filename
	 * @return
	 */
	protected FileAppender newFileAppender(LogConfiguration logConfig, FileAppender appenderTemplate) throws Exception {
		// 新建一个文件appender, layout为设置, 文件追加方式为添加.
		String conversionPattern = getConversionPattern(logConfig, appenderTemplate);			
		logConfig.setConversionPattern(conversionPattern);// 格式转换

		// 新建一个格式转换实例
		PatternLayout newLayout = new PatternLayout(conversionPattern);
		// 文件名
		String filename = getFileName(logConfig, appenderTemplate);
		// 日期格式
		String datePattern = getDatePattern(appenderTemplate);
		
		long maxFileSize = getMaxFileSize(appenderTemplate);
		int maxBackups = getMaxBackups(appenderTemplate);
	    
		// 创建
		return createFileAppender(newLayout, filename, datePattern, 
				logConfig, appenderTemplate, maxFileSize, maxBackups);
	}
	
	private long getMaxFileSize(FileAppender appenderTemplate) {
		if (appenderTemplate instanceof RollingFileAppender) {
			return ((RollingFileAppender)appenderTemplate).getMaximumFileSize();
		} else {
		    return 0;
		}
	}
	
	private int getMaxBackups(FileAppender appenderTemplate) {
		if (appenderTemplate instanceof RollingFileAppender) {
			return ((RollingFileAppender)appenderTemplate).getMaxBackupIndex();
		} else {
		    return 0;
		}
	}
	
	/**
	 * 创建
	 * 
	 * @param newLayout
	 * @param filename
	 * @param datePattern
	 * @param logConfig
	 * @param appenderTemplate
	 * @return
	 * @throws IOException
	 */
	protected FileAppender createFileAppender(PatternLayout newLayout, String filename, 
			String datePattern, LogConfiguration logConfig, FileAppender appenderTemplate,
			long maxFileSize, int maxBackups) throws IOException {
		FileAppender appender = null; // APPENDER
		if (appenderTemplate instanceof RollingFileAppender) {
			RollingFileAppenderAdapter rollappender = new RollingFileAppenderAdapter(newLayout, filename);
			rollappender.setMaxBackupIndex(maxBackups);
			rollappender.setMaximumFileSize(maxFileSize);
			
			appender = rollappender; // SET
		} else {
		    appender = new DailyRollingFileAppenderAdapter(newLayout, filename, 
				    datePattern);
		}
				
		appender.setAppend(true);
		appender.setEncoding(appenderTemplate.getEncoding());			
		appender.setName(appenderTemplate.getName() + "-" 
				         + logConfig.getDirname());
		appender.setImmediateFlush(true); // 立即写	
		appender.activateOptions();// 激活
		
		return appender;
	}
	
	/**
	 * 日期格式
	 * 
	 * @param appenderTemplate
	 * @return
	 */
	protected String getDatePattern(FileAppender appenderTemplate) {
		String datePattern = null;			
        if (appenderTemplate instanceof DailyRollingFileAppender) {
        	datePattern = ((DailyRollingFileAppender)appenderTemplate).getDatePattern();
		} else {
			datePattern = ".yyyy-MM-dd";
		}
        
        return datePattern;
	}
	
	/**
	 * 格式
	 * 
	 * @param logConfig
	 * @param appenderTemplate
	 * @return
	 */
	protected String getConversionPattern(LogConfiguration logConfig, FileAppender appenderTemplate) {
		PatternLayout layout = (PatternLayout)appenderTemplate.getLayout();
		String conversionPattern = logConfig.getConversionPattern();
		if (conversionPattern == null) {
			conversionPattern = layout.getConversionPattern();
		}
		
		return conversionPattern;
	}
	
	/**
	 * 文件名
	 * 
	 * @param logConfig
	 * @param appenderTemplate
	 * @return
	 */
	protected String getFileName(LogConfiguration logConfig, FileAppender appenderTemplate) {
		String dirname = logConfig.getDirname();
		dirname = dirname != null ? dirname : "common-log";//如果日志目录为空, 默认为公共目录
		
		String filename = null;
		File file = new File(appenderTemplate.getFile());
		
		String parent = logConfig.isWithTempleteInSameDir() ? file.getParent() 
				: (file.getParent() == null ? file.getParent() 
						: new File(file.getParent()).getParent());
		if (parent == null) {
			filename = dirname  + "/" + file.getName();
		} else {
			filename =  parent
		        + "/" + dirname  + "/" + file.getName();
		}
		
		return filename;
	}
		
	/**
     * 获取当前线程相关联的appender
     * @return
     */
	public List<Appender> getAppenders(Thread thread) {
		List<LogConfiguration> logConfigs = getLogConfigurations(thread);
		if (logConfigs == null) {
			// 如果没有设置新的文件名, 则用默认(一般为log4j.properties设置),
			// 该规则适用于第三方软件包				
			return null;
		}
		
		List<Appender> configAppenders = new ArrayList<Appender>();
		
		for (LogConfiguration logConfig : logConfigs) {
			Appender appender = logConfig.getLogObjectAppender();
			configAppenders.add(appender);
		}
		
		return configAppenders;
	}
	
	public List<LogConfiguration> getLogConfigurations(Thread thread) {
		List<LogConfiguration> logConfigs = logConfigurations.get(thread);
		if (logConfigs == null) {
			// 如果没有设置新的文件名, 则用默认(一般为log4j.properties设置),
			// 该规则适用于第三方软件包
			return null;
		}
		for (Iterator<LogConfiguration> it = logConfigs.iterator(); it.hasNext();) {
			LogConfiguration logConfig = it.next();
			// 删除已经销毁的配置
			if (logConfig.isDestroyed()) {
				it.remove();
				
				Appender appender = logConfig.getLogObjectAppender();
				if (appender != null) {
					try {
					    appender.close();
					} catch (Throwable t) {						
					}
				}
			}
		}
		return logConfigs;
	}
	    
    /**
     * 注册当前线程的日志文件名
     * @param filename
     */
    public void register(LogConfiguration logConfiguration, Thread thread) {    	
    	List<LogConfiguration> configs = logConfigurations.get(thread);
		if (configs == null) {
			configs = Collections.synchronizedList(new ArrayList<LogConfiguration>());
			logConfigurations.put(thread, configs);
		}
		
		if (!configs.contains(logConfiguration)) {
			configs.add(logConfiguration);
		}
		
		Collection<LogConfiguration> relas = logConfiguration.getAllRelationConfigs();
		if (relas != null) {
			for (LogConfiguration rela : relas) {
				if (!configs.contains(rela)) {
					configs.add(rela);
				}
			}
		}
    }    
    
    public void unregister(Thread thread) {
    	logConfigurations.remove(thread);
    }
        
    public void removeLogConfiguration(LogConfiguration logConfiguration, Thread thread) {
    	List<LogConfiguration> configs = logConfigurations.get(thread);
		if (configs != null) {
			configs.remove(logConfiguration);
			Collection<LogConfiguration> relas = logConfiguration.getAllRelationConfigs();
			if (relas != null) {
				for (LogConfiguration rela : relas) {
					configs.remove(rela);
				}
			}
		}
    }
    
    public void tryCloseLogAppender(LogConfiguration logConfiguration) {
    	Appender appender = logConfiguration.getLogObjectAppender();
		if (appender != null) {
			appender.close();
		}
		
		Collection<LogConfiguration> relas = logConfiguration.getAllRelationConfigs();
		if (relas != null) {
			for (LogConfiguration rela : relas) {
				appender = rela.getLogObjectAppender();
				if (appender != null) {
					appender.close();
				}
			}
		}
    }
}
